﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Moria.Common;
using System.Collections.Generic;
using System.Windows.Media.Imaging;
using System.Windows.Controls.Primitives;
using Moria.Common.Model;
using Redbrick.Silverlight.Common.Collections;
using System.ComponentModel;
using System.Diagnostics;

namespace Moria.Silverlight
{

	public class MoriaCanvas : Panel, IScrollInfo
	{

		#region Consts & Settings


		#endregion Consts & Settings

		#region Declarations

		private static Dictionary<string, WriteableBitmap> _imageCache = new Dictionary<string, WriteableBitmap>();
		private static readonly WriteableBitmap _tileSource = null;

		public static readonly DependencyProperty TileHeightProperty = DependencyProperty.Register("TileHeight", typeof(int), typeof(MapGrid), new PropertyMetadata(32));
		public static readonly DependencyProperty TileWidthProperty = DependencyProperty.Register("TileWidth", typeof(int), typeof(MapGrid), new PropertyMetadata(32));

		private double xOffset;
		private double yOffset;
		private Size _Extent;
		private Size _viewport;

		private Canvas _canvas;
		private QuadTree<Drawable> _floor;

		#endregion Declarations

		#region Constructor
		static MoriaCanvas()
		{
			_tileSource = new WriteableBitmap(0, 0).FromResource("Assets/Tiles/32x32.png");

		}
		public MoriaCanvas()
		{
			this.DataContextChanged += new DependencyPropertyChangedEventHandler(this.OnDataContextChanged);
		}

		void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
		{
			Init();
		}

		private void Init()
		{
			Width = this.Moria.Cave.Width * this.TileWidth;
			Height = this.Moria.Cave.Height * this.TileHeight;

			_canvas = new Canvas()
			{
				Width = this.Width,
				Height = this.Height,
			};

			this.Children.Clear();
			this.Children.Add(_canvas);

			this._floor = new QuadTree<Drawable>(new Rect(0, 0, Width, Height));
			foreach (CaveFloor floor in this.Moria.Cave)
			{
				floor.TileHeight = this.TileHeight;
				floor.TileWidth = this.TileWidth;
				
				this._floor.Insert(floor);
			}
		}

		#endregion Constructor

		#region Methods
		private WriteableBitmap GetTile(int x, int y)
		{
			string key = x + "," + y;
			if (!_imageCache.ContainsKey(key))
			{
				WriteableBitmap bmp = new WriteableBitmap(this.TileWidth, this.TileHeight);
				if (x >= 0 && x >= 0)
				{
					Rect src = new Rect(x * this.TileWidth, y * this.TileHeight, this.TileWidth, this.TileHeight);
					bmp.Blit(new Rect(0, 0, this.TileWidth, this.TileHeight), _tileSource, src);
				}
				else
				{
					bmp.Clear(Colors.Green);
				}
				_imageCache[key] = bmp;
			}

			return _imageCache[key];
		}
		private WriteableBitmap GetFloorTile(CaveFloor floor)
		{
			int x = 0;
			int y = 0;
			switch (floor.FloorType)
			{
				default:
					Debug.WriteLine("unknown floor tile:" + floor.Description);
					x = -1; y = -1; break;
				case FloorType.BoundaryWall: x = 1; y = 26; break;
				case FloorType.DarkFloor: x = 1; y = 23; break;
				case FloorType.GraniteWall: x = 76; y = 22; break;
				case FloorType.Corridor:
				case FloorType.LightFloor: x = 4; y = 23; break;
				case FloorType.MagamaWall: x = 52; y = 22; break;
				case FloorType.QuartzWall: x = 58; y = 22; break;
				case FloorType.BlockedFloor: x = 121; y = 121; break;
			}

			WriteableBitmap img = GetTile(x, y);
			return img;
		}

		protected override Size MeasureOverride(Size availableSize)
		{
			if (this._floor == null) Init();
			if (DesignerProperties.IsInDesignTool) return Size.Empty;
			base.MeasureOverride(availableSize);

			if (this.Moria != null && this.Moria.Cave != null)
			{


				availableSize = new Size(this.TileWidth * this.Moria.Cave.Width,
												 this.TileHeight * this.Moria.Cave.Height);
				if (availableSize != this._viewport)
				{
					SetViewportSize(availableSize);
				}

			}
			return availableSize;
		}
		protected override Size ArrangeOverride(Size finalSize)
		{
			base.ArrangeOverride(finalSize);
			_canvas.Children.Clear();

			List<Drawable> toDraw = _floor.Query(GetVisibleRect());

			foreach (CaveFloor floor in toDraw)
			{
				WriteableBitmap wb = GetFloorTile(floor);
				Image img = new Image() { Source = wb };
				Canvas.SetLeft(img, floor.Rect.Left);
				Canvas.SetTop(img, floor.Rect.Top);

				_canvas.Children.Add(img);
			}
			return finalSize;
		}
		void SetViewportSize(Size s)
		{
			if (s != this._viewport)
			{
				_viewport = s;
				OnScrollChanged();
			}
		}
		Rect GetVisibleRect()
		{
			// Add a bit of extra around the edges so we are sure to create nodes that have a tiny bit showing.
			double xstart = (this.HorizontalOffset - TileWidth);
			double ystart = (this.VerticalOffset - TileHeight);
			double xend = (this.HorizontalOffset + (_viewport.Width + (2 * TileWidth)));
			double yend = (this.VerticalOffset + (_viewport.Height + (2 * TileHeight)));
			return new Rect(xstart, ystart, xend - xstart, yend - ystart);
		}

		private void OnScrollChanged()
		{
		}

		public void LineDown() { SetVerticalOffset(VerticalOffset + TileHeight); }
		public void LineUp() { SetVerticalOffset(VerticalOffset - TileHeight); }
		public void LineLeft() { SetHorizontalOffset(HorizontalOffset - TileWidth); }
		public void LineRight() { SetHorizontalOffset(HorizontalOffset + TileWidth); }

		public void MouseWheelDown() { SetVerticalOffset(VerticalOffset + WheelSize); }
		public void MouseWheelLeft() { SetVerticalOffset(VerticalOffset + WheelSize); }
		public void MouseWheelRight() { SetHorizontalOffset(HorizontalOffset + WheelSize); }
		public void MouseWheelUp() { SetHorizontalOffset(HorizontalOffset - WheelSize); }

		public void PageDown() { SetVerticalOffset(VerticalOffset + ViewportHeight); }
		public void PageLeft() { SetVerticalOffset(VerticalOffset - ViewportHeight); }
		public void PageRight() { SetHorizontalOffset(HorizontalOffset + ViewportWidth); }
		public void PageUp() { SetHorizontalOffset(HorizontalOffset - ViewportWidth); }

		public void SetHorizontalOffset(double offset)
		{
			offset = Math.Max(0, Math.Min(offset, ExtentWidth - ViewportWidth));
			if (offset != xOffset)
			{
				xOffset = offset;
				InvalidateArrange();
			}
		}
		public void SetVerticalOffset(double offset)
		{
			offset = Math.Max(0, Math.Min(offset, ExtentHeight - ViewportHeight));
			if (offset != yOffset)
			{
				yOffset = offset;
				InvalidateArrange();
			}
		}

		#endregion Methods

		#region Events


		#endregion Events

		#region Properties

		private int WheelSize { get { return 3 * TileHeight; } }

		public int TileWidth
		{
			get { return (int)GetValue(TileWidthProperty); }
			set { SetValue(TileWidthProperty, value); }
		}
		public int TileHeight
		{
			get { return (int)GetValue(TileHeightProperty); }
			set { SetValue(TileHeightProperty, value); }
		}

		private TheGame Moria { get { return this.DataContext as TheGame; } }

		public ScrollViewer ScrollOwner { get; set; }
		public bool CanHorizontallyScroll { get; set; }
		public bool CanVerticallyScroll { get; set; }

		public double ExtentHeight { get { return _Extent.Height; } }
		public double ExtentWidth { get { return _Extent.Width; } }

		public double HorizontalOffset { get { return xOffset; } }
		public double VerticalOffset { get { return this.yOffset; } }

		public double ViewportHeight { get { return _viewport.Height; } }
		public double ViewportWidth { get { return _viewport.Width; } }
		#endregion Properties

		// make the visual visible for the rectangle area
		public Rect MakeVisible(UIElement visual, Rect rectangle)
		{
			//throw new NotImplementedException();
			return Rect.Empty;
		}
	}
}
